//
//  FLInterface.m
//  Pods
//
//  Created by LinXiaoBin on 16/3/15.
//
//

#import "FLInterfaceBase.h"
#import <AFNetworking/AFNetworking.h>


NSString *const kProtocolVersion1_0 = @"1.0";
NSString *const kProtocolVersion1_1 = @"1.1";
NSString *const kProtocolVersion2_0 = @"2.0";
NSString *const kProtocolVersion3_0 = @"3.0";

typedef NS_ENUM(NSInteger, FLRequestType) {
    FLRequestTypePost = 0,
    FLRequestTypeGet = 1,
};

@interface FLInterfaceBase() {
    NSURLSessionDataTask *_afURLSessionTask;
    AFURLSessionManager *_sessionManager;
    
    BOOL _isCancel;
    FLRequestType _requestType;
}

@property (nonatomic, strong) NSDictionary *header;
@property (nonatomic, strong) NSData *data;
@property (nonatomic, assign) NSInteger tryReloadTimes;

@end

@implementation FLInterfaceBase

- (instancetype)initWithDelegate:(id<FLInterfaceResponseDelegate>)delegate
{
    return [self initWithDelegate:delegate succeedBlock:nil failedBlock:nil];
}

- (instancetype)initWithSucceedBlock:(FLInterfaceSucceedBlock_t)succeedBlock failedBlock:(FLInterfaceFailedBlock_t)failedBlock
{
    return [self initWithDelegate:nil succeedBlock:succeedBlock failedBlock:failedBlock];
}

- (instancetype)initWithDelegate:(id<FLInterfaceResponseDelegate>)delegate succeedBlock:(FLInterfaceSucceedBlock_t)succeedBlock failedBlock:(FLInterfaceFailedBlock_t)failedBlock
{
    self = [super init];
    if (self) {
        _delegate = delegate;
        _succeedBlock = [succeedBlock copy];
        _failedBlock = [failedBlock copy];
    }
    return self;
}

- (void)dealloc
{
    NSLog(@"%@ ==== dealloc", [self class]);
}

#pragma mark - public
- (void)postURL:(NSString *)url header:(NSDictionary *)header dictionaryBody:(NSDictionary *)dictionary
{
    NSData *data = nil;
    if (dictionary) {
        data = [NSJSONSerialization dataWithJSONObject:dictionary options:NSJSONWritingPrettyPrinted error:NULL];
    }
    
    [self postURL:url header:header dataBody:data];
}

- (void)postURL:(NSString *)url header:(NSDictionary *)header stringBody:(NSString *)string encoding:(NSStringEncoding)encoding
{
    NSData *data = nil;
    if (string) {
        data = [string dataUsingEncoding:encoding];
    }
    [self postURL:url header:header dataBody:data];
}

- (void)postURL:(NSString *)urlString header:(NSDictionary<NSString *, NSString *> *)header dataBody:(NSData *)data
{
    [self sendRequestURL:urlString header:header dataBody:data type:FLRequestTypePost];
}

- (void)getURL:(NSString *)urlString header:(NSDictionary *)header
{
    [self sendRequestURL:urlString header:header dataBody:nil type:FLRequestTypeGet];
}

- (void)sendRequestURL:(NSString *)urlString header:(NSDictionary<NSString *, NSString *> *)header dataBody:(NSData *)data type:(FLRequestType)type
{
    _requestType = type;
    
    self.urlString = urlString;
    self.header = header;
    self.data = data;
    
    BOOL didRequest = NO;
    if (urlString) {
        NSURL *url = [NSURL URLWithString:urlString];
        self.url = url;
        if (url) {
            NSString *method = @"POST";
            if (type == FLRequestTypeGet) {
                method = @"GET";
            }
            
            NSURLRequest *request = [self URLRequestWithURL:url header:header dataBody:data method:method];
            _sessionManager = [self sessionManager];
            typeof(self) __weak weakSelf = self;
            NSURLSessionDataTask *dataTask = [_sessionManager dataTaskWithRequest:request uploadProgress:nil downloadProgress:nil completionHandler:^(NSURLResponse * _Nonnull response, id  _Nullable responseObject, NSError * _Nullable error) {
                [weakSelf onURLResponse:response responseObject:responseObject error:error];
            }];
            [dataTask resume];
            
            didRequest = YES;
        }
    }
    
    if (!didRequest) {
        [self afRequestFailed:nil error:[self errorWithCode:FLInterfaceErrorCodeURLError message:nil]];
    }
}

- (NSURLRequest *)URLRequestWithURL:(NSURL *)url header:(NSDictionary<NSString *, NSString *> *)header dataBody:(NSData *)body method:(NSString *)method
{
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    [header enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSString * _Nonnull obj, BOOL * _Nonnull stop) {
        [request setValue:obj forHTTPHeaderField:key];
    }];
    request.HTTPMethod = method;
    if (body) {
        request.HTTPBody = body;
    }
    return request;
}

- (AFURLSessionManager *)sessionManager;
{
    NSString *proxyIP = self.proxyIPString;
    NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    if (proxyIP) {
        configuration.connectionProxyDictionary = @{(id)kCFNetworkProxiesHTTPEnable: @(1),
                                                    (id)kCFStreamPropertyHTTPProxyHost: proxyIP
                                                    };
    }
    
    AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
    manager.responseSerializer = [AFHTTPResponseSerializer serializer];
    
    return manager;
}

- (void)onURLResponse:(NSURLResponse * _Nonnull)response responseObject:(id  _Nullable)responseObject error:(NSError * _Nullable)error
{
    NSDictionary *header = nil;
    if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
        header = [(NSHTTPURLResponse *)response allHeaderFields];
    }
    
    if (error) {
        [self afRequestFailed:header error:error];
    } else {
        [self afRequestFinished:header responseObject:responseObject];
    }
}

- (void)postHttpsURL:(NSString *)urlString header:(NSDictionary<NSString *, NSString *> *)header dataBody:(NSData *)data {
    _requestType = FLRequestTypePost;
    
    self.urlString = urlString;
    self.header = header;
    self.data = data;
    
    BOOL didRequest = NO;
    if (urlString) {
        NSURL *url = [NSURL URLWithString:urlString];
        self.url = url;
        if (url) {
            NSString *proxyIP = self.proxyIPString;
            NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
            if (proxyIP) {
                configuration.connectionProxyDictionary = @{(id)kCFNetworkProxiesHTTPEnable: @(1),
                                                            (id)kCFStreamPropertyHTTPProxyHost: proxyIP
                                                            };
            }
            
            NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
            [header enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSString * _Nonnull obj, BOOL * _Nonnull stop) {
                [request setValue:obj forHTTPHeaderField:key];
            }];
            request.HTTPMethod = @"POST";
            request.HTTPBody = data;
            
            AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
            _sessionManager = manager;
            manager.responseSerializer = [AFHTTPResponseSerializer serializer];
            
            AFSecurityPolicy * securityPolicy = [AFSecurityPolicy defaultPolicy];
            
            //allowInvalidCertificates 是否允许无效证书（也就是自建的证书），默认为NO
            //如果是需要验证自建证书，需要设置为YES
            securityPolicy.allowInvalidCertificates = YES;
            
            //validatesDomainName 是否需要验证域名，默认为YES；
            //假如证书的域名与你请求的域名不一致，需把该项设置为NO；如设成NO的话，即服务器使用其他可信任机构颁发的证书，也可以建立连接，这个非常危险，建议打开。
            //置为NO，主要用于这种情况：客户端请求的是子域名，而证书上的是另外一个域名。因为SSL证书上的域名是独立的，假如证书上注册的域名是www.google.com，那么mail.google.com是无法验证通过的；当然，有钱可以注册通配符的域名*.google.com，但这个还是比较贵的。
            //如置为NO，建议自己添加对应域名的校验逻辑。
            securityPolicy.validatesDomainName = NO;
            
            //validatesCertificateChain 是否验证整个证书链，默认为YES
            //设置为YES，会将服务器返回的Trust Object上的证书链与本地导入的证书进行对比，这就意味着，假如你的证书链是这样的：
            //GeoTrust Global CA
            //    Google Internet Authority G2
            //        *.google.com
            //那么，除了导入*.google.com之外，还需要导入证书链上所有的CA证书（GeoTrust Global CA, Google Internet Authority G2）；
            //如是自建证书的时候，可以设置为YES，增强安全性；假如是信任的CA所签发的证书，则建议关闭该验证，因为整个证书链一一比对是完全没有必要（请查看源代码）；
            // securityPolicy.validatesCertificateChain = NO;
            manager.securityPolicy = securityPolicy;
            
            typeof(self) __weak weakSelf = self;
            NSURLSessionDataTask *dataTask = [manager dataTaskWithRequest:request uploadProgress:nil downloadProgress:nil completionHandler:^(NSURLResponse * _Nonnull response, id  _Nullable responseObject, NSError * _Nullable error) {
                NSDictionary *header = nil;
                if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
                    header = [(NSHTTPURLResponse *)response allHeaderFields];
                }
                
                if (error) {
                    [weakSelf afRequestFailed:header error:error];
                } else {
                    [weakSelf afRequestFinished:header responseObject:responseObject];
                }
            }];
            [dataTask resume];
            
            didRequest = YES;
        }
    }
    
    if (!didRequest) {
        [self afRequestFailed:nil error:[self errorWithCode:FLInterfaceErrorCodeURLError message:nil]];
    }
}

- (BOOL)tryReload
{
    if (self.tryReloadTimes < self.maxTryReloadTimes) {
        self.tryReloadTimes++;
        
        switch (_requestType) {
            case FLRequestTypePost:
                [self postURL:self.urlString header:self.header dataBody:self.data];
                break;
                
            default:
                return NO;
        }
        return YES;
    } else {
        return NO;
    }
}

- (void)failedRequest:(NSString *)url code:(NSInteger)code message:(NSString *)message
{
    NSError *error = [self errorWithCode:code message:message];
    [self afRequestFailed:nil error:error];
}

- (void)failedRequest:(NSError *)error
{
    [self afRequestFailed:nil error:error];
}

- (NSError *)errorWithDomain:(NSString *)domain code:(NSInteger)code message:(NSString *)message
{
    return [NSError errorWithDomain:domain?domain:@"" code:code userInfo:@{@"ResultMessage": message?message:@""}];
}

- (NSError *)errorWithCode:(NSInteger)code message:(NSString *)message
{
    return [self errorWithDomain:self.urlString code:code message:message];
}

- (void)cancelAndClearDelegate
{
    _isCancel = YES;
    
    if (_afURLSessionTask && _afURLSessionTask.state != NSURLSessionTaskStateCompleted) {
        [_afURLSessionTask cancel];
        _afURLSessionTask = nil;
    }
    _sessionManager = nil;
    
    self.delegate = nil;
    [self didRequestFinished];
}

#pragma mark - private

- (void)didRequestFinished
{
    id<FLInterfaceManagerDelegate> delegate = self.managerDelegate;
    if ([delegate respondsToSelector:@selector(flInterfaceDidRequestFinished:)]) {
        [delegate flInterfaceDidRequestFinished:self];
    }
}

- (void)succeedWithResponseHeader:(NSDictionary *)responseHeader responseObject:(id)responseObject
{
    id<FLInterfaceResponseDelegate>delegate = self.delegate;
    if ([delegate respondsToSelector:@selector(flInterface:didRequestFinished:responseObject:)]) {
        [delegate flInterface:self didRequestFinished:responseHeader responseObject:responseObject];
    } else {
        FLInterfaceSucceedBlock_t succeedBlock = self.succeedBlock;
        if (succeedBlock) {
            succeedBlock(self, responseHeader, responseObject);
        }
    }
}

- (void)failedWithResponseHeader:(NSDictionary *)responseHeader error:(NSError *)error
{
    id<FLInterfaceResponseDelegate>delegate = self.delegate;
    if ([delegate respondsToSelector:@selector(flInterface:didRequestFailed:error:)]) {
        [delegate flInterface:self didRequestFailed:responseHeader error:error];
    } else {
        FLInterfaceFailedBlock_t failedBlock = self.failedBlock;
        if (failedBlock) {
            failedBlock(self, responseHeader, error);
        }
    }
}

- (void)afRequestFinished:(NSDictionary *)responseHeader responseObject:(id)responseObject
{
    id result = responseObject;
    NSError *error = nil;
    
    id<FLResponseSerializerDelegate> serializer = self.responseSerializer;
    if ([serializer respondsToSelector:@selector(flInterface:serializerResponseHeader:responseObject:error:)]) {
        result = [serializer flInterface:self serializerResponseHeader:responseHeader responseObject:responseObject error:&error];
    }
    
    if (error == nil) {
        [self succeedWithResponseHeader:responseHeader responseObject:result];
    } else {
        [self failedWithResponseHeader:responseHeader error:error];
    }
    
    [self didRequestFinished];
}

- (void)afRequestFailed:(NSDictionary *)responseHeader error:(NSError *)error
{
    if (_isCancel) {
        return;
    }
    
    // 添加日志
    NSLog(@"responseHeader:\n%@error:%@", responseHeader, error);
    
#if !TARGET_OS_SIMULATOR
    if (![self tryReload])
#endif
    {
        [self failedWithResponseHeader:responseHeader error:error];        
        [self didRequestFinished];
    }
}

@end
